/*-------------------------------------------------------------------------
 *
 * pg_type.c
 *	  支持pg_type关系操作的常规例程
 *
 * Portions Copyright (c) 1996-2022, PostgreSQL Global Development Group
 * Portions Copyright (c) 1994, Regents of the University of California
 *
 *
 * IDENTIFICATION
 *	  src/backend/catalog/pg_type.c
 *
 *-------------------------------------------------------------------------
 */
#include "postgres.h"

#include "access/htup_details.h"
#include "access/table.h"
#include "access/xact.h"
#include "catalog/binary_upgrade.h"
#include "catalog/catalog.h"
#include "catalog/dependency.h"
#include "catalog/indexing.h"
#include "catalog/objectaccess.h"
#include "catalog/pg_collation.h"
#include "catalog/pg_namespace.h"
#include "catalog/pg_proc.h"
#include "catalog/pg_type.h"
#include "commands/typecmds.h"
#include "mb/pg_wchar.h"
#include "miscadmin.h"
#include "parser/scansup.h"
#include "utils/acl.h"
#include "utils/builtins.h"
#include "utils/fmgroids.h"
#include "utils/lsyscache.h"
#include "utils/rel.h"
#include "utils/syscache.h"

static char *fc_makeUniqueTypeName(const char *fc_typeName, Oid fc_typeNamespace,
								bool fc_tryOriginal);

/* 可能由 pg_upgrade_support 函数设置 */
Oid			binary_upgrade_next_pg_type_oid = InvalidOid;

/* ----------------------------------------------------------------
 *		TypeShellMake
 *
 *		该过程将一个“外壳”元组插入到pg_type关系中。
 *		插入的类型元组具有有效但虚假的值，并且其
 *		“typisdefined”字段为false，表明它并没有真正定义。
 *
 *		这用于确保在目录中存在一个元组。 类型的I/O
 *		函数将链接到这个元组。当发出完整的
 *		CREATE TYPE命令时，虚假的值将被
 *		正确的值替换，并且“typisdefined”将被设置为true。
 * ----------------------------------------------------------------
 */
ObjectAddress TypeShellMake(const char *fc_typeName, Oid fc_typeNamespace, Oid fc_ownerId)
{
	Relation	pg_type_desc;
	TupleDesc	fc_tupDesc;
	int			fc_i;
	HeapTuple	fc_tup;
	Datum		fc_values[Natts_pg_type];
	bool		fc_nulls[Natts_pg_type];
	Oid			fc_typoid;
	NameData	fc_name;
	ObjectAddress fc_address;

	Assert(PointerIsValid(fc_typeName));

	/*
	 * 打开pg_type
	 */
	pg_type_desc = table_open(TypeRelationId, RowExclusiveLock);
	fc_tupDesc = pg_type_desc->rd_att;

	/*
	 * 初始化我们的 *nulls 和 *values 数组
	 */
	for (fc_i = 0; fc_i < Natts_pg_type; ++fc_i)
	{
		fc_nulls[fc_i] = false;
		fc_values[fc_i] = (Datum) NULL;	/* 多余，但安全 */
	}

	/*
	 * 用类型名称和虚假值初始化*values
	 *
	 * 表示细节与int4相同……只要它们一致，实际上它们是什么并不重要。
	 * 还要注意，我们将其typtype设置为TYPTYPE_PSEUDO，作为额外保障，以确保
	 * 它不会被误认为一个可用的类型。
	 */
	namestrcpy(&fc_name, fc_typeName);
	fc_values[Anum_pg_type_typname - 1] = NameGetDatum(&fc_name);
	fc_values[Anum_pg_type_typnamespace - 1] = ObjectIdGetDatum(fc_typeNamespace);
	fc_values[Anum_pg_type_typowner - 1] = ObjectIdGetDatum(fc_ownerId);
	fc_values[Anum_pg_type_typlen - 1] = Int16GetDatum(sizeof(int32));
	fc_values[Anum_pg_type_typbyval - 1] = BoolGetDatum(true);
	fc_values[Anum_pg_type_typtype - 1] = CharGetDatum(TYPTYPE_PSEUDO);
	fc_values[Anum_pg_type_typcategory - 1] = CharGetDatum(TYPCATEGORY_PSEUDOTYPE);
	fc_values[Anum_pg_type_typispreferred - 1] = BoolGetDatum(false);
	fc_values[Anum_pg_type_typisdefined - 1] = BoolGetDatum(false);
	fc_values[Anum_pg_type_typdelim - 1] = CharGetDatum(DEFAULT_TYPDELIM);
	fc_values[Anum_pg_type_typrelid - 1] = ObjectIdGetDatum(InvalidOid);
	fc_values[Anum_pg_type_typsubscript - 1] = ObjectIdGetDatum(InvalidOid);
	fc_values[Anum_pg_type_typelem - 1] = ObjectIdGetDatum(InvalidOid);
	fc_values[Anum_pg_type_typarray - 1] = ObjectIdGetDatum(InvalidOid);
	fc_values[Anum_pg_type_typinput - 1] = ObjectIdGetDatum(F_SHELL_IN);
	fc_values[Anum_pg_type_typoutput - 1] = ObjectIdGetDatum(F_SHELL_OUT);
	fc_values[Anum_pg_type_typreceive - 1] = ObjectIdGetDatum(InvalidOid);
	fc_values[Anum_pg_type_typsend - 1] = ObjectIdGetDatum(InvalidOid);
	fc_values[Anum_pg_type_typmodin - 1] = ObjectIdGetDatum(InvalidOid);
	fc_values[Anum_pg_type_typmodout - 1] = ObjectIdGetDatum(InvalidOid);
	fc_values[Anum_pg_type_typanalyze - 1] = ObjectIdGetDatum(InvalidOid);
	fc_values[Anum_pg_type_typalign - 1] = CharGetDatum(TYPALIGN_INT);
	fc_values[Anum_pg_type_typstorage - 1] = CharGetDatum(TYPSTORAGE_PLAIN);
	fc_values[Anum_pg_type_typnotnull - 1] = BoolGetDatum(false);
	fc_values[Anum_pg_type_typbasetype - 1] = ObjectIdGetDatum(InvalidOid);
	fc_values[Anum_pg_type_typtypmod - 1] = Int32GetDatum(-1);
	fc_values[Anum_pg_type_typndims - 1] = Int32GetDatum(0);
	fc_values[Anum_pg_type_typcollation - 1] = ObjectIdGetDatum(InvalidOid);
	fc_nulls[Anum_pg_type_typdefaultbin - 1] = true;
	fc_nulls[Anum_pg_type_typdefault - 1] = true;
	fc_nulls[Anum_pg_type_typacl - 1] = true;

	/* 是否使用二进制升级覆盖 pg_type.oid？ */
	if (IsBinaryUpgrade)
	{
		if (!OidIsValid(binary_upgrade_next_pg_type_oid))
			ereport(ERROR,
					(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
					 errmsg("pg_type OID value not set when in binary upgrade mode")));

		fc_typoid = binary_upgrade_next_pg_type_oid;
		binary_upgrade_next_pg_type_oid = InvalidOid;
	}
	else
	{
		fc_typoid = GetNewOidWithIndex(pg_type_desc, TypeOidIndexId,
									Anum_pg_type_oid);
	}

	fc_values[Anum_pg_type_oid - 1] = ObjectIdGetDatum(fc_typoid);

	/*
	 * 创建一个新的类型元组
	 */
	fc_tup = heap_form_tuple(fc_tupDesc, fc_values, fc_nulls);

	/*
	 * 在关系中插入元组并获取元组的oid。
	 */
	CatalogTupleInsert(pg_type_desc, fc_tup);

	/*
	 * 创建依赖关系。在引导模式下我们可以/必须跳过这一点。
	 */
	if (!IsBootstrapProcessingMode())
		GenerateTypeDependencies(fc_tup,
								 pg_type_desc,
								 NULL,
								 NULL,
								 0,
								 false,
								 false,
								 true,	/* 创建扩展依赖关系 */
								 false);

	/* 新的外壳类型的创建后钩子 */
	InvokeObjectPostCreateHook(TypeRelationId, fc_typoid, 0);

	ObjectAddressSet(fc_address, TypeRelationId, fc_typoid);

	/*
	 * 清理并返回类型-oid
	 */
	heap_freetuple(fc_tup);
	table_close(pg_type_desc, RowExclusiveLock);

	return fc_address;
}

/* ----------------------------------------------------------------
 *		TypeCreate
 *
 *		这完成了定义新类型所需的所有必要工作。
 *
 *		返回分配给新类型的ObjectAddress。
 *		如果newTypeOid为零（正常情况），则创建一个新的OID；
 *		否则我们将使用确切的OID。
 * ----------------------------------------------------------------
 */
ObjectAddress TypeCreate(Oid fc_newTypeOid,
		   const char *fc_typeName,
		   Oid fc_typeNamespace,
		   Oid fc_relationOid,		/* 仅用于关系行类型 */
		   char fc_relationKind,	/* 同上 */
		   Oid fc_ownerId,
		   int16 fc_internalSize,
		   char fc_typeType,
		   char fc_typeCategory,
		   bool fc_typePreferred,
		   char fc_typDelim,
		   Oid fc_inputProcedure,
		   Oid fc_outputProcedure,
		   Oid fc_receiveProcedure,
		   Oid fc_sendProcedure,
		   Oid fc_typmodinProcedure,
		   Oid fc_typmodoutProcedure,
		   Oid fc_analyzeProcedure,
		   Oid fc_subscriptProcedure,
		   Oid fc_elementType,
		   bool fc_isImplicitArray,
		   Oid fc_arrayType,
		   Oid fc_baseType,
		   const char *fc_defaultTypeValue,	/* 人类可读的表示 */
		   char *fc_defaultTypeBin,	/* 处理过的表示 */
		   bool fc_passedByValue,
		   char fc_alignment,
		   char fc_storage,
		   int32 fc_typeMod,
		   int32 fc_typNDims,		/* 基本类型的数组维度 */
		   bool fc_typeNotNull,
		   Oid fc_typeCollation)
{
	Relation	pg_type_desc;
	Oid			fc_typeObjectId;
	bool		fc_isDependentType;
	bool		fc_rebuildDeps = false;
	Acl		   *fc_typacl;
	HeapTuple	fc_tup;
	bool		fc_nulls[Natts_pg_type];
	bool		fc_replaces[Natts_pg_type];
	Datum		fc_values[Natts_pg_type];
	NameData	fc_name;
	int			fc_i;
	ObjectAddress fc_address;

	/*
	 * 我们假设调用方已单独验证了参数，但没有
	 * 检查不良组合。
	 *
	 * 验证大小规格：要么为正（固定长度），要么为-1
	 * （变长）或-2（cstring）。
	 */
	if (!(fc_internalSize > 0 ||
		  fc_internalSize == -1 ||
		  fc_internalSize == -2))
		ereport(ERROR,
				(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
				 errmsg("invalid type internal size %d",
						fc_internalSize)));

	if (fc_passedByValue)
	{
		/*
		 * 传值类型必须具有固定长度，且是fetch_att()和store_att_byval()支持的
		 * 值之一；并且对齐也最好一致。所有此代码必须与
		 * access/tupmacs.h匹配！
		 */
		if (fc_internalSize == (int16) sizeof(char))
		{
			if (fc_alignment != TYPALIGN_CHAR)
				ereport(ERROR,
						(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
						 errmsg("alignment \"%c\" is invalid for passed-by-value type of size %d",
								fc_alignment, fc_internalSize)));
		}
		else if (fc_internalSize == (int16) sizeof(int16))
		{
			if (fc_alignment != TYPALIGN_SHORT)
				ereport(ERROR,
						(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
						 errmsg("alignment \"%c\" is invalid for passed-by-value type of size %d",
								fc_alignment, fc_internalSize)));
		}
		else if (fc_internalSize == (int16) sizeof(int32))
		{
			if (fc_alignment != TYPALIGN_INT)
				ereport(ERROR,
						(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
						 errmsg("alignment \"%c\" is invalid for passed-by-value type of size %d",
								fc_alignment, fc_internalSize)));
		}
#if SIZEOF_DATUM == 8
		else if (fc_internalSize == (int16) sizeof(Datum))
		{
			if (fc_alignment != TYPALIGN_DOUBLE)
				ereport(ERROR,
						(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
						 errmsg("alignment \"%c\" is invalid for passed-by-value type of size %d",
								fc_alignment, fc_internalSize)));
		}
#endif
		else
			ereport(ERROR,
					(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
					 errmsg("internal size %d is invalid for passed-by-value type",
							fc_internalSize)));
	}
	else
	{
		/* 变长类型必须具有int对齐或更好 */
		if (fc_internalSize == -1 &&
			!(fc_alignment == TYPALIGN_INT || fc_alignment == TYPALIGN_DOUBLE))
			ereport(ERROR,
					(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
					 errmsg("alignment \"%c\" is invalid for variable-length type",
							fc_alignment)));
		/* cstring必须具有字符对齐 */
		if (fc_internalSize == -2 && !(fc_alignment == TYPALIGN_CHAR))
			ereport(ERROR,
					(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
					 errmsg("alignment \"%c\" is invalid for variable-length type",
							fc_alignment)));
	}

	/* 仅变长类型可以进行烘烤 */
	if (fc_storage != TYPSTORAGE_PLAIN && fc_internalSize != -1)
		ereport(ERROR,
				(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
				 errmsg("fixed-size types must have storage PLAIN")));

	/*
	 * 如果这是一个隐式创建的数组类型，或者
	 * 如果它是一个非复合类型的关系行类型，则这是一个依赖类型。
	 * 对于这样的类型，我们将保留ACL为空，并且
	 * 我们将跳过创建一些依赖关系记录，因为
	 * 通过被依赖的类型或关系已经存在依赖。
	 * （注意：这与GenerateTypeDependencies中的某些行为紧密交织。）
	 */
	fc_isDependentType = fc_isImplicitArray ||
		(OidIsValid(fc_relationOid) && fc_relationKind != RELKIND_COMPOSITE_TYPE);

	/*
	 * 初始化heap_form_tuple或heap_modify_tuple所需的数组
	 */
	for (fc_i = 0; fc_i < Natts_pg_type; ++fc_i)
	{
		fc_nulls[fc_i] = false;
		fc_replaces[fc_i] = true;
		fc_values[fc_i] = (Datum) 0;
	}

	/*
	 * 插入数据值
	 */
	namestrcpy(&fc_name, fc_typeName);
	fc_values[Anum_pg_type_typname - 1] = NameGetDatum(&fc_name);
	fc_values[Anum_pg_type_typnamespace - 1] = ObjectIdGetDatum(fc_typeNamespace);
	fc_values[Anum_pg_type_typowner - 1] = ObjectIdGetDatum(fc_ownerId);
	fc_values[Anum_pg_type_typlen - 1] = Int16GetDatum(fc_internalSize);
	fc_values[Anum_pg_type_typbyval - 1] = BoolGetDatum(fc_passedByValue);
	fc_values[Anum_pg_type_typtype - 1] = CharGetDatum(fc_typeType);
	fc_values[Anum_pg_type_typcategory - 1] = CharGetDatum(fc_typeCategory);
	fc_values[Anum_pg_type_typispreferred - 1] = BoolGetDatum(fc_typePreferred);
	fc_values[Anum_pg_type_typisdefined - 1] = BoolGetDatum(true);
	fc_values[Anum_pg_type_typdelim - 1] = CharGetDatum(fc_typDelim);
	fc_values[Anum_pg_type_typrelid - 1] = ObjectIdGetDatum(fc_relationOid);
	fc_values[Anum_pg_type_typsubscript - 1] = ObjectIdGetDatum(fc_subscriptProcedure);
	fc_values[Anum_pg_type_typelem - 1] = ObjectIdGetDatum(fc_elementType);
	fc_values[Anum_pg_type_typarray - 1] = ObjectIdGetDatum(fc_arrayType);
	fc_values[Anum_pg_type_typinput - 1] = ObjectIdGetDatum(fc_inputProcedure);
	fc_values[Anum_pg_type_typoutput - 1] = ObjectIdGetDatum(fc_outputProcedure);
	fc_values[Anum_pg_type_typreceive - 1] = ObjectIdGetDatum(fc_receiveProcedure);
	fc_values[Anum_pg_type_typsend - 1] = ObjectIdGetDatum(fc_sendProcedure);
	fc_values[Anum_pg_type_typmodin - 1] = ObjectIdGetDatum(fc_typmodinProcedure);
	fc_values[Anum_pg_type_typmodout - 1] = ObjectIdGetDatum(fc_typmodoutProcedure);
	fc_values[Anum_pg_type_typanalyze - 1] = ObjectIdGetDatum(fc_analyzeProcedure);
	fc_values[Anum_pg_type_typalign - 1] = CharGetDatum(fc_alignment);
	fc_values[Anum_pg_type_typstorage - 1] = CharGetDatum(fc_storage);
	fc_values[Anum_pg_type_typnotnull - 1] = BoolGetDatum(fc_typeNotNull);
	fc_values[Anum_pg_type_typbasetype - 1] = ObjectIdGetDatum(fc_baseType);
	fc_values[Anum_pg_type_typtypmod - 1] = Int32GetDatum(fc_typeMod);
	fc_values[Anum_pg_type_typndims - 1] = Int32GetDatum(fc_typNDims);
	fc_values[Anum_pg_type_typcollation - 1] = ObjectIdGetDatum(fc_typeCollation);

	/*
	 * 初始化此类型的默认二进制值。检查null值
	 * 当然。
	 */
	if (fc_defaultTypeBin)
		fc_values[Anum_pg_type_typdefaultbin - 1] = CStringGetTextDatum(fc_defaultTypeBin);
	else
		fc_nulls[Anum_pg_type_typdefaultbin - 1] = true;

	/*
	 * 初始化此类型的默认值。
	 */
	if (fc_defaultTypeValue)
		fc_values[Anum_pg_type_typdefault - 1] = CStringGetTextDatum(fc_defaultTypeValue);
	else
		fc_nulls[Anum_pg_type_typdefault - 1] = true;

	/*
	 * 也初始化类型的ACL。但依赖类型没有ACL。
	 */
	if (fc_isDependentType)
		fc_typacl = NULL;
	else
		fc_typacl = get_user_default_acl(OBJECT_TYPE, fc_ownerId,
									  fc_typeNamespace);
	if (fc_typacl != NULL)
		fc_values[Anum_pg_type_typacl - 1] = PointerGetDatum(fc_typacl);
	else
		fc_nulls[Anum_pg_type_typacl - 1] = true;

	/*
	 * 打开pg_type并准备插入或更新一行。
	 *
	 * 注意：在引导模式下，更新将无法正确工作；
	 * 但我们不期望在引导模式下覆盖任何外壳类型。
	 */
	pg_type_desc = table_open(TypeRelationId, RowExclusiveLock);

	fc_tup = SearchSysCacheCopy2(TYPENAMENSP,
							  CStringGetDatum(fc_typeName),
							  ObjectIdGetDatum(fc_typeNamespace));
	if (HeapTupleIsValid(fc_tup))
	{
		Form_pg_type fc_typform = (Form_pg_type) GETSTRUCT(fc_tup);

		/*
		 * 检查该类型是否已经定义。然而，它可能作为外壳类型存在。
		 */
		if (fc_typform->typisdefined)
			ereport(ERROR,
					(errcode(ERRCODE_DUPLICATE_OBJECT),
					 errmsg("type \"%s\" already exists", fc_typeName)));

		/*
		 * shell 类型必须由同一所有者创建
		 */
		if (fc_typform->typowner != fc_ownerId)
			aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_TYPE, fc_typeName);

		/* 如果调用者想强制使用 OID，将会有问题 */
		if (OidIsValid(fc_newTypeOid))
			elog(ERROR, "cannot assign new OID to existing shell type");

		fc_replaces[Anum_pg_type_oid - 1] = false;

		/*
		 * 可以更新现有的 shell 类型元组
		 */
		fc_tup = heap_modify_tuple(fc_tup,
								RelationGetDescr(pg_type_desc),
								fc_values,
								fc_nulls,
								fc_replaces);

		CatalogTupleUpdate(pg_type_desc, &fc_tup->t_self, fc_tup);

		fc_typeObjectId = fc_typform->oid;

		fc_rebuildDeps = true;		/* 消除 shell 类型的依赖关系 */
	}
	else
	{
		/* 如果调用者请求，强制使用 OID */
		if (OidIsValid(fc_newTypeOid))
			fc_typeObjectId = fc_newTypeOid;
		/* 如果提供，则使用二进制升级覆盖 pg_type.oid。 */
		else if (IsBinaryUpgrade)
		{
			if (!OidIsValid(binary_upgrade_next_pg_type_oid))
				ereport(ERROR,
						(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
						 errmsg("pg_type OID value not set when in binary upgrade mode")));

			fc_typeObjectId = binary_upgrade_next_pg_type_oid;
			binary_upgrade_next_pg_type_oid = InvalidOid;
		}
		else
		{
			fc_typeObjectId = GetNewOidWithIndex(pg_type_desc, TypeOidIndexId,
											  Anum_pg_type_oid);
		}

		fc_values[Anum_pg_type_oid - 1] = ObjectIdGetDatum(fc_typeObjectId);

		fc_tup = heap_form_tuple(RelationGetDescr(pg_type_desc),
							  fc_values, fc_nulls);

		CatalogTupleInsert(pg_type_desc, fc_tup);
	}

	/*
	 * 创建依赖关系。在引导模式下我们可以/必须跳过这一点。
	 */
	if (!IsBootstrapProcessingMode())
		GenerateTypeDependencies(fc_tup,
								 pg_type_desc,
								 (fc_defaultTypeBin ?
								  stringToNode(fc_defaultTypeBin) :
								  NULL),
								 fc_typacl,
								 fc_relationKind,
								 fc_isImplicitArray,
								 fc_isDependentType,
								 true,	/* 创建扩展依赖关系 */
								 fc_rebuildDeps);

	/* 新类型的创建后钩子 */
	InvokeObjectPostCreateHook(TypeRelationId, fc_typeObjectId, 0);

	ObjectAddressSet(fc_address, TypeRelationId, fc_typeObjectId);

	/*
	 * 完成
	 */
	table_close(pg_type_desc, RowExclusiveLock);

	return fc_address;
}

/*
 * GenerateTypeDependencies: 构建类型所需的依赖关系
 *
 * 这个函数需要了解的类型信息大部分是通过新的 pg_type 行传递的，typeTuple。我们还让调用者传递 pg_type 关系，以便我们能够轻松访问行的元组描述符。
 *
 * 虽然这个函数能够从元组中提取 defaultExpr 和 typacl，但这样做相对昂贵，调用者可能已经掌握了这些值。如果方便，请传递这些值，否则传递 NULL。（typacl 实际上是 "Acl *"，但我们将其声明为 "void *"，以避免在 pg_type.h 中包含 acl.h。）
 *
 * relationKind 和 isImplicitArray 同样从元组中推导出相对昂贵，因此我们让调用者传递这两个值（它们不是可选的）。
 *
 * 如果这是隐式数组或关系行类型，则 isDependentType 为 true；这意味着它不需要对所有者等的自己的依赖。
 *
 * 如果我们在扩展脚本中，并且 makeExtensionDep 为 true（且 isDependentType 不为 true），则我们创建一个扩展成员依赖关系。创建新类型或替换 shell 类型时，makeExtensionDep 应为 true，但在现有类型上进行 ALTER TYPE 时则不应如此。传递 false 会让类型的扩展成员关系保持不变。
 *
 * 如果这是一个预存在的类型，rebuild 应该为 true。我们将删除现有的依赖关系并从零开始重建它们。这在 ALTER TYPE 时以及替换 shell 类型时是必需的。不过，我们不会删除任何现有的扩展依赖关系；因此，如果 makeExtensionDep 也为 true，并且我们在扩展脚本中，将会发生错误，除非该类型已经属于当前扩展。这是我们在替换 shell 类型时希望的行为，这是唯一一个两个标志都为 true 的情况。
 */
void GenerateTypeDependencies(HeapTuple fc_typeTuple,
						 Relation fc_typeCatalog,
						 Node *fc_defaultExpr,
						 void *fc_typacl,
						 char fc_relationKind, /* 仅用于关系行类型 */
						 bool fc_isImplicitArray,
						 bool fc_isDependentType,
						 bool fc_makeExtensionDep,
						 bool fc_rebuild)
{
	Form_pg_type fc_typeForm = (Form_pg_type) GETSTRUCT(fc_typeTuple);
	Oid			fc_typeObjectId = fc_typeForm->oid;
	Datum		fc_datum;
	bool		fc_isNull;
	ObjectAddress fc_myself,
				fc_referenced;
	ObjectAddresses *fc_addrs_normal;

	/* 如果调用者没有传递，则提取 defaultExpr */
	if (fc_defaultExpr == NULL)
	{
		fc_datum = heap_getattr(fc_typeTuple, Anum_pg_type_typdefaultbin,
							 RelationGetDescr(fc_typeCatalog), &fc_isNull);
		if (!fc_isNull)
			fc_defaultExpr = stringToNode(TextDatumGetCString(fc_datum));
	}
	/* 如果调用者没有传递，则提取 typacl */
	if (fc_typacl == NULL)
	{
		fc_datum = heap_getattr(fc_typeTuple, Anum_pg_type_typacl,
							 RelationGetDescr(fc_typeCatalog), &fc_isNull);
		if (!fc_isNull)
			fc_typacl = DatumGetAclPCopy(fc_datum);
	}

	/* 如果是重建，首先清空旧的依赖关系，除了扩展依赖 */
	if (fc_rebuild)
	{
		deleteDependencyRecordsFor(TypeRelationId, fc_typeObjectId, true);
		deleteSharedDependencyRecordsFor(TypeRelationId, fc_typeObjectId, 0);
	}

	ObjectAddressSet(fc_myself, TypeRelationId, fc_typeObjectId);

	/*
	 * 在命名空间、所有者、ACL、扩展上建立依赖关系。
	 *
	 * 跳过依赖类型的这些，因为它会通过其依赖的类型或关系间接具有这些依赖关系。
	 */

	/* 所有正常依赖关系的占位符 */
	fc_addrs_normal = new_object_addresses();

	if (!fc_isDependentType)
	{
		ObjectAddressSet(fc_referenced, NamespaceRelationId,
						 fc_typeForm->typnamespace);
		recordDependencyOn(&fc_myself, &fc_referenced, DEPENDENCY_NORMAL);

		recordDependencyOnOwner(TypeRelationId, fc_typeObjectId,
								fc_typeForm->typowner);

		recordDependencyOnNewAcl(TypeRelationId, fc_typeObjectId, 0,
								 fc_typeForm->typowner, fc_typacl);

		if (fc_makeExtensionDep)
			recordDependencyOnCurrentExtension(&fc_myself, fc_rebuild);
	}

	/* 对 I/O 和支持函数的正常依赖 */
	if (OidIsValid(fc_typeForm->typinput))
	{
		ObjectAddressSet(fc_referenced, ProcedureRelationId, fc_typeForm->typinput);
		add_exact_object_address(&fc_referenced, fc_addrs_normal);
	}

	if (OidIsValid(fc_typeForm->typoutput))
	{
		ObjectAddressSet(fc_referenced, ProcedureRelationId, fc_typeForm->typoutput);
		add_exact_object_address(&fc_referenced, fc_addrs_normal);
	}

	if (OidIsValid(fc_typeForm->typreceive))
	{
		ObjectAddressSet(fc_referenced, ProcedureRelationId, fc_typeForm->typreceive);
		add_exact_object_address(&fc_referenced, fc_addrs_normal);
	}

	if (OidIsValid(fc_typeForm->typsend))
	{
		ObjectAddressSet(fc_referenced, ProcedureRelationId, fc_typeForm->typsend);
		add_exact_object_address(&fc_referenced, fc_addrs_normal);
	}

	if (OidIsValid(fc_typeForm->typmodin))
	{
		ObjectAddressSet(fc_referenced, ProcedureRelationId, fc_typeForm->typmodin);
		add_exact_object_address(&fc_referenced, fc_addrs_normal);
	}

	if (OidIsValid(fc_typeForm->typmodout))
	{
		ObjectAddressSet(fc_referenced, ProcedureRelationId, fc_typeForm->typmodout);
		add_exact_object_address(&fc_referenced, fc_addrs_normal);
	}

	if (OidIsValid(fc_typeForm->typanalyze))
	{
		ObjectAddressSet(fc_referenced, ProcedureRelationId, fc_typeForm->typanalyze);
		add_exact_object_address(&fc_referenced, fc_addrs_normal);
	}

	if (OidIsValid(fc_typeForm->typsubscript))
	{
		ObjectAddressSet(fc_referenced, ProcedureRelationId, fc_typeForm->typsubscript);
		add_exact_object_address(&fc_referenced, fc_addrs_normal);
	}

	/* 从域到其基础类型的正常依赖。 */
	if (OidIsValid(fc_typeForm->typbasetype))
	{
		ObjectAddressSet(fc_referenced, TypeRelationId, fc_typeForm->typbasetype);
		add_exact_object_address(&fc_referenced, fc_addrs_normal);
	}

	/*
	 * 从域到其排序规则的正常依赖。我们知道默认排序规则是固定的，因此无需记录它。
	 */
	if (OidIsValid(fc_typeForm->typcollation) &&
		fc_typeForm->typcollation != DEFAULT_COLLATION_OID)
	{
		ObjectAddressSet(fc_referenced, CollationRelationId, fc_typeForm->typcollation);
		add_exact_object_address(&fc_referenced, fc_addrs_normal);
	}

	record_object_address_dependencies(&fc_myself, fc_addrs_normal, DEPENDENCY_NORMAL);
	free_object_addresses(fc_addrs_normal);

	/* 对默认表达式的正常依赖。 */
	if (fc_defaultExpr)
		recordDependencyOnExpr(&fc_myself, fc_defaultExpr, NIL, DEPENDENCY_NORMAL);

	/*
	 * 如果类型是关系的行类型，则将其标记为与关系内部依赖，*除非*它是独立的复合类型关系。在后一种情况下，我们必须反转依赖关系。
	 *
	 * 在前一种情况下，这允许在关系被删除时自动删除该类型，而不以其他方式进行。在后一种情况下，当然我们获得相反的效果。
	 */
	if (OidIsValid(fc_typeForm->typrelid))
	{
		ObjectAddressSet(fc_referenced, RelationRelationId, fc_typeForm->typrelid);

		if (fc_relationKind != RELKIND_COMPOSITE_TYPE)
			recordDependencyOn(&fc_myself, &fc_referenced, DEPENDENCY_INTERNAL);
		else
			recordDependencyOn(&fc_referenced, &fc_myself, DEPENDENCY_INTERNAL);
	}

	/*
	 * 如果类型是隐式创建的数组类型，则将其标记为与元素类型内部依赖。否则，如果它有元素类型，依赖关系是正常的。
	 */
	if (OidIsValid(fc_typeForm->typelem))
	{
		ObjectAddressSet(fc_referenced, TypeRelationId, fc_typeForm->typelem);
		recordDependencyOn(&fc_myself, &fc_referenced,
						   fc_isImplicitArray ? DEPENDENCY_INTERNAL : DEPENDENCY_NORMAL);
	}
}

/*
 * RenameTypeInternal
 *		此函数重命名一个类型，以及任何相关的数组类型。
 *
 * 调用者必须已经检查权限。
 *
 * 当前这用于重命名表行类型和 ALTER TYPE RENAME TO 命令。
 */
void RenameTypeInternal(Oid fc_typeOid, const char *fc_newTypeName, Oid fc_typeNamespace)
{
	Relation	pg_type_desc;
	HeapTuple	fc_tuple;
	Form_pg_type fc_typ;
	Oid			fc_arrayOid;
	Oid			fc_oldTypeOid;

	pg_type_desc = table_open(TypeRelationId, RowExclusiveLock);

	fc_tuple = SearchSysCacheCopy1(TYPEOID, ObjectIdGetDatum(fc_typeOid));
	if (!HeapTupleIsValid(fc_tuple))
		elog(ERROR, "cache lookup failed for type %u", fc_typeOid);
	fc_typ = (Form_pg_type) GETSTRUCT(fc_tuple);

	/* 我们不应该在这里更改模式 */
	Assert(fc_typeNamespace == fc_typ->typnamespace);

	fc_arrayOid = fc_typ->typarray;

	/* 检查冲突的类型名称。 */
	fc_oldTypeOid = GetSysCacheOid2(TYPENAMENSP, Anum_pg_type_oid,
								 CStringGetDatum(fc_newTypeName),
								 ObjectIdGetDatum(fc_typeNamespace));

	/*
	 * 如果有一个，看看它是否是一个自动生成的数组类型，如果是，则将其重命名。 （但我们必须跳过 shell 类型，因为 moveArrayTypeName 在这种情况下会做错误的事情。）
	 * 否则，我们至少可以给出比唯一索引冲突更友好的错误。
	 */
	if (OidIsValid(fc_oldTypeOid))
	{
		if (get_typisdefined(fc_oldTypeOid) &&
			moveArrayTypeName(fc_oldTypeOid, fc_newTypeName, fc_typeNamespace))
			 /* 成功避免了问题 */ ;
		else
			ereport(ERROR,
					(errcode(ERRCODE_DUPLICATE_OBJECT),
					 errmsg("type \"%s\" already exists", fc_newTypeName)));
	}

	/* 好吧，进行重命名——元组是一个副本，所以可以随意修改 */
	namestrcpy(&(fc_typ->typname), fc_newTypeName);

	CatalogTupleUpdate(pg_type_desc, &fc_tuple->t_self, fc_tuple);

	InvokeObjectPostAlterHook(TypeRelationId, fc_typeOid, 0);

	heap_freetuple(fc_tuple);
	table_close(pg_type_desc, RowExclusiveLock);

	/*
	 * 如果类型具有数组类型，递归处理该数组。但是，如果我们已经在上面重命名了该数组类型（例如，将 "foo" 重命名为 "_foo"），则不需要做更多事情。
	 */
	if (OidIsValid(fc_arrayOid) && fc_arrayOid != fc_oldTypeOid)
	{
		char	   *fc_arrname = makeArrayTypeName(fc_newTypeName, fc_typeNamespace);

		RenameTypeInternal(fc_arrayOid, fc_arrname, fc_typeNamespace);
		pfree(fc_arrname);
	}
}


/*
 * makeArrayTypeName
 *	  - 给定基本类型名称，为其创建数组类型名称
 *
 * 调用者负责释放结果
 */
char * makeArrayTypeName(const char *fc_typeName, Oid fc_typeNamespace)
{
	char	   *fc_arr;

	fc_arr = fc_makeUniqueTypeName(fc_typeName, fc_typeNamespace, false);
	if (fc_arr == NULL)
		ereport(ERROR,
				(errcode(ERRCODE_DUPLICATE_OBJECT),
				 errmsg("could not form array type name for type \"%s\"",
						fc_typeName)));

	return fc_arr;
}



/*
 * moveArrayTypeName
 *	  - 尝试重新分配用户想要使用的数组类型名称。
 *
 * 发现给定的类型名称已经存在（具有给定的 OID）。如果它是一个自动生成的数组类型，则改变数组类型的名称以避免冲突。这允许用户创建类型 "foo" 然后是类型 "_foo" 而不会出现问题。（当然，如果两个后端尝试同时创建类似名称的类型，会有竞争条件，但最糟糕的结果只是一次不必要的失败——如果类型创建因名称冲突失败，我们在这里所做的任何事情都会被回滚。）
 *
 * 请注意，这必须在调用 makeArrayTypeName 确定新类型的数组类型名称之前调用；否则后者肯定会选择相同的名称。
 *
 * 如果成功移动类型则返回 true，失败则返回 false。
 *
 * 如果给定的类型是一个外壳类型，我们也返回 true。在这种情况下，类型没有被重新命名，但仍然可以预期 TypeCreate 会成功。这种行为对于大多数调用者是方便的——那些需要区分外壳类型情况的必须自己进行 typisdefined 测试。
 */
bool moveArrayTypeName(Oid fc_typeOid, const char *fc_typeName, Oid fc_typeNamespace)
{
	Oid			fc_elemOid;
	char	   *fc_newname;

	/* 如果是外壳类型，我们不需要做任何事情。 */
	if (!get_typisdefined(fc_typeOid))
		return true;

	/* 如果它不是自动生成的数组类型，则无法更改。 */
	fc_elemOid = get_element_type(fc_typeOid);
	if (!OidIsValid(fc_elemOid) ||
		get_array_type(fc_elemOid) != fc_typeOid)
		return false;

	/*
	 * 好的，使用 makeArrayTypeName 来选择一个未使用的名称修改。
	 * 注意，由于 makeArrayTypeName 是一个迭代过程，这将
	 * 生成一个名称，如果我们要创建的冲突类型已经存在，
	 * 它可能在第一次时产生该名称。
	 */
	fc_newname = makeArrayTypeName(fc_typeName, fc_typeNamespace);

	/* 应用重命名 */
	RenameTypeInternal(fc_typeOid, fc_newname, fc_typeNamespace);

	/*
	 * 我们必须增加命令计数器，以便后续对
	 * makeArrayTypeName 的使用看到我们刚做的事情，
	 * 不会选择相同的名称。
	 */
	CommandCounterIncrement();

	pfree(fc_newname);

	return true;
}


/*
 * makeMultirangeTypeName
 *	  - 给定一个范围类型名称，为其创建一个多范围类型名称
 *
 * 调用者负责释放结果
 */
char * makeMultirangeTypeName(const char *fc_rangeTypeName, Oid fc_typeNamespace)
{
	char	   *fc_buf;
	char	   *fc_rangestr;

	/*
	 * 如果范围类型名称包含“range”，则将其更改为
	 * “multirange”。否则在末尾添加“_multirange”。
	 */
	fc_rangestr = strstr(fc_rangeTypeName, "range");
	if (fc_rangestr)
	{
		char	   *fc_prefix = pnstrdup(fc_rangeTypeName, fc_rangestr - fc_rangeTypeName);

		fc_buf = psprintf("%s%s%s", fc_prefix, "multi", fc_rangestr);
	}
	else
		fc_buf = psprintf("%s_multirange", pnstrdup(fc_rangeTypeName, NAMEDATALEN - 12));

	/* 在 NAMEDATALEN-1 字节处剪切 */
	fc_buf[pg_mbcliplen(fc_buf, strlen(fc_buf), NAMEDATALEN - 1)] = '\0';

	if (SearchSysCacheExists2(TYPENAMENSP,
							  CStringGetDatum(fc_buf),
							  ObjectIdGetDatum(fc_typeNamespace)))
		ereport(ERROR,
				(errcode(ERRCODE_DUPLICATE_OBJECT),
				 errmsg("type \"%s\" already exists", fc_buf),
				 errdetail("Failed while creating a multirange type for type \"%s\".", fc_rangeTypeName),
				 errhint("You can manually specify a multirange type name using the \"multirange_type_name\" attribute.")));

	return pstrdup(fc_buf);
}

/*
 * makeUniqueTypeName
 *		为潜在的新类型生成唯一名称
 *
 * 给定一个 typeName，通过在前面添加下划线返回一个新的 palloc' 名称，
 * 直到产生一个不冲突的名称。
 *
 * 如果 tryOriginal，先尝试零个下划线。
 */
static char * fc_makeUniqueTypeName(const char *fc_typeName, Oid fc_typeNamespace, bool fc_tryOriginal)
{
	int			fc_i;
	int			fc_namelen;
	char		fc_dest[NAMEDATALEN];

	Assert(strlen(fc_typeName) <= NAMEDATALEN - 1);

	if (fc_tryOriginal &&
		!SearchSysCacheExists2(TYPENAMENSP,
							   CStringGetDatum(fc_typeName),
							   ObjectIdGetDatum(fc_typeNamespace)))
		return pstrdup(fc_typeName);

	/*
	 * 这个想法是根据需要在前面添加下划线，直到我们生成一个
	 * 不与任何内容冲突的名称…… 
	 */
	fc_namelen = strlen(fc_typeName);
	for (fc_i = 1; fc_i < NAMEDATALEN - 1; fc_i++)
	{
		fc_dest[fc_i - 1] = '_';
		strlcpy(fc_dest + fc_i, fc_typeName, NAMEDATALEN - fc_i);
		if (fc_namelen + fc_i >= NAMEDATALEN)
			truncate_identifier(fc_dest, NAMEDATALEN, false);

		if (!SearchSysCacheExists2(TYPENAMENSP,
								   CStringGetDatum(fc_dest),
								   ObjectIdGetDatum(fc_typeNamespace)))
			return pstrdup(fc_dest);
	}

	return NULL;
}
